{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "keras.ipynb",
      "version": "0.3.2",
      "provenance": [],
      "private_outputs": true,
      "collapsed_sections": [],
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "CCQY7jpBfMur"
      },
      "source": [
        "##### Copyright 2018 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "cellView": "form",
        "colab_type": "code",
        "id": "z6X9omPnfO_h",
        "colab": {}
      },
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "F1xIRPtY0E1w"
      },
      "source": [
        "# Keras"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "VyOjQZHhZxaA"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs/blob/master/site/ru/r1/guide/keras.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Запусти в Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs/blob/master/site/ru/r1/guide/keras.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />Изучай код на GitHub</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "JWjueNizcxUU",
        "colab_type": "text"
      },
      "source": [
        "Note: Вся информация в этом разделе переведена с помощью русскоговорящего Tensorflow сообщества на общественных началах. Поскольку этот перевод не является официальным, мы не гарантируем что он на 100% аккуратен и соответствует [официальной документации на английском языке](https://www.tensorflow.org/?hl=en). Если у вас есть предложение как исправить этот перевод, мы будем очень рады увидеть pull request в [tensorflow/docs](https://github.com/tensorflow/docs) репозиторий GitHub. Если вы хотите помочь сделать документацию по Tensorflow лучше (сделать сам перевод или проверить перевод подготовленный кем-то другим), напишите нам на [docs-ru@tensorflow.org list](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ru)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "ViAXWoKlZ8s6"
      },
      "source": [
        "Keras - это высокоуровневый API для создания моделей глубокого обучения.\n",
        "Он используется для быстрого создания прототипов, сложных исследований,\n",
        "а также для создания приложений. Три ключевые примущества Keras API:\n",
        "\n",
        "- *Простота в использовании*<br>\n",
        "  Keras имеет простой интерфейс, оптимизированный для большинства\n",
        "  распространенных задач глубокого обучения. Также он дает конкретные подсказки как быстро\n",
        "  исправить возможные ошибки\n",
        "- *Модульность*<br>\n",
        "  Модели Keras строятся при помощи объединения нескольких простых модулей, каждый из которых\n",
        "  может быть настроен независимо, и не накладывает каких либо значительных ограничений\n",
        "- *Легко расширить модель*<br> Ты можешь создавать свои собственные модули,\n",
        "  чтобы свободно выражать свои идеи для исследования. Создавай новые слои,\n",
        "  функции потерь и разрабатывай современные модели глубокого обучения"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "IsK5aF2xZ-40"
      },
      "source": [
        "## Импортируем tf.keras\n",
        "\n",
        "`tf.keras` - это реализация [спецификации Keras API](https://keras.io){:.external} в TensorFlow.\n",
        "Это высокоуровневый API для построения\n",
        "моделей глубокого обучения с первоклассной поддержкой\n",
        "функционала TensorFlow, например [eager execution](#eager_execution),\n",
        "конвееры `tf.data` и алгоритмы оценки [Estimators](./estimators.md).\n",
        "`tf.keras` делает TensorFlow простым в использовании, не теряя в гибкости и производительности.\n",
        "\n",
        "Чтобы начать, просто импортируй `tf.keras` после TensorFlow в начале кода:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "Qoc055N3wiUG",
        "colab": {}
      },
      "source": [
        "# Используй pyyaml если будешь сохранять в формате YAML\n",
        "!pip install pyyaml"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "TgPcBFru0E1z",
        "colab": {}
      },
      "source": [
        "from __future__ import absolute_import, division, print_function, unicode_literals, unicode_literals\n",
        "\n",
        "import tensorflow as tf\n",
        "from tensorflow.keras import layers\n",
        "\n",
        "print(tf.VERSION)\n",
        "print(tf.keras.__version__)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "lj03RamP0E13"
      },
      "source": [
        "`tf.keras` может запускать любой совместимый с Keras код, но тем не менее помни:\n",
        "\n",
        "* Версия `tf.keras` в последнем релизе TensorFlow может быть не самой\n",
        "последнией версией `keras`, доступной в PyPI. Всегда проверяй версию при помощи `tf.keras.__version__`\n",
        "* Когда ты [сохраняешь только веса модели](#weights_only), `tf.keras` по умолчанию\n",
        "  сохраняет их в [формате контрольной точки](./checkpoints.md). Используй `save_format='h5'`\n",
        "  чтобы сохранять как HDF5"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "_WUw7R_zfpIh"
      },
      "source": [
        "## Построим простую модель"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "7e1LPcXx0gR6"
      },
      "source": [
        "### Последовательная модель\n",
        "\n",
        "В Keras мы используем *слои* для построения *моделей*. Обычно модель - это граф, состоящий из нескольких слоев. Самый распространенный тип модели это стэк идущих друг за другом слоев - последовательная модель `tf.keras.Sequential`.\n",
        "\n",
        "Для начала давай построим простую полносвязную сеть (или многослойный перцептрон):"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "WM-DUVQB0E14",
        "colab": {}
      },
      "source": [
        "model = tf.keras.Sequential()\n",
        "# Добавим в нашу модель слой Dense из 64 блоков:\n",
        "model.add(layers.Dense(64, activation='relu'))\n",
        "# И еще один:\n",
        "model.add(layers.Dense(64, activation='relu'))\n",
        "# Также добавим слой softmax с 10 выводящими блоками:\n",
        "model.add(layers.Dense(10, activation='softmax'))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "-ztyTipu0E18"
      },
      "source": [
        "### Конфигурация слоев\n",
        "\n",
        "Существует много разных слоев `tf.keras.layers` с общими параметрами конфигурации:\n",
        "\n",
        "* `activation`: Устанавливает функцию активации для данного слоя. Этот параметр должен указываться в имени встроенной функции или использоваться как вызываемый объект. По умолчанию активация не используется\n",
        "* `kernel_initializer` и `bias_initializer`: Инициализация, которая создает веса данного слоя (ядро kernel и смещение bias). Этот параметр используется как имя или вызываемый объект. По умолчанию используется инициализатор `\"Glorot uniform\"`\n",
        "* `kernel_regularizer` и `bias_regularizer`: Регуляризация, которая применяется к весам слоя (ядро kernel и смещение bias),  например L1 или L2. По умолчанию не используется\n",
        "\n",
        "Следующий код используется для построения `tf.keras.layers.Dense` с настройкой конфигурации каждого слоя:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "MlL7PBtp0E19",
        "colab": {}
      },
      "source": [
        "# Создаем сигмоидный слой:\n",
        "layers.Dense(64, activation='sigmoid')\n",
        "# Или:\n",
        "layers.Dense(64, activation=tf.sigmoid)\n",
        "\n",
        "# Линейный слой с регуляризацией L1 с коэффицентом 0.01 примененная к ядру матрицы:\n",
        "layers.Dense(64, kernel_regularizer=tf.keras.regularizers.l1(0.01))\n",
        "\n",
        "# Линейный слой с регуляризацией L2 с коэффицентом 0.01 примененная к вектору смещения (bias):\n",
        "layers.Dense(64, bias_regularizer=tf.keras.regularizers.l2(0.01))\n",
        "\n",
        "# Линейный слой с ядром, инициализированным в случайной прямоугольный матрице:\n",
        "layers.Dense(64, kernel_initializer='orthogonal')\n",
        "\n",
        "# Линейный слой с вектором смещения, инициализированным с коэффицентом 2:\n",
        "layers.Dense(64, bias_initializer=tf.keras.initializers.constant(2.0))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "9NR6reyk0E2A"
      },
      "source": [
        "## Обучение и оценка\n",
        "\n",
        "### Настроим конфигурацию обучения\n",
        "\n",
        "После того как модель построена давай обозначим процесс обучения с помощью метода `compile`:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "sJ4AOn090E2A",
        "colab": {}
      },
      "source": [
        "model = tf.keras.Sequential([\n",
        "# Добавим в нашу модель слой `Dense` из 64 блоков:\n",
        "layers.Dense(64, activation='relu', input_shape=(32,)),\n",
        "# И еще один:\n",
        "layers.Dense(64, activation='relu'),\n",
        "# Также добавим слой softmax с 10 выводящими блоками:\n",
        "layers.Dense(10, activation='softmax')])\n",
        "\n",
        "model.compile(optimizer=tf.train.AdamOptimizer(0.001),\n",
        "              loss='categorical_crossentropy',\n",
        "              metrics=['accuracy'])"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "HG-RAa9F0E2D"
      },
      "source": [
        "У `tf.keras.Model.compile` есть три важных аргумента:\n",
        "\n",
        "* `optimizer`: Этот объект определяет процедуру обучения. Мы будем использовать оптимизаторы из модуля `tf.train`, например такие как `tf.train.AdamOptimizer`, `tf.train.RMSPropOptimizer` и `tf.train.GradientDescentOptimizer`\n",
        "* `loss`: Эта функция минимизации для решения задач оптимизации. Самыми распространенными выборами этого аргумента являются среднеквадратическое отклонение (`mse`), `categorical_crossentropy`, и `binary_crossentropy`. Функции потерь обозначены по имени или используются как вызываемые объекты из модуля `tf.keras.losses`\n",
        "* `metrics`: Используются для наблюдения за процессом обучения. Используются как строки или вызываемые объекты модуля `tf.keras.metrics`\n",
        "\n",
        "Ниже пример конфигурации модели для обучения:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "St4Mgdar0E2E",
        "colab": {}
      },
      "source": [
        "# Настраиваем регрессию модели, используем среднеквадратическое отклонение\n",
        "model.compile(optimizer=tf.train.AdamOptimizer(0.01),\n",
        "              loss='mse',       # среднеквадратическое отклонение\n",
        "              metrics=['mae'])  # средняя абсолютная ошибка\n",
        "\n",
        "# Настраиваем модель для классификации по категориям\n",
        "model.compile(optimizer=tf.train.RMSPropOptimizer(0.01),\n",
        "              loss=tf.keras.losses.categorical_crossentropy,\n",
        "              metrics=[tf.keras.metrics.categorical_accuracy])"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "yjI5rbi80E2G"
      },
      "source": [
        "### Используем данные NumPy\n",
        "\n",
        "При работе с небольшими датасетами мы будем использовать загружаемые в память массивы данных [NumPy](https://www.numpy.org/){:.external} для обучения и оценки моделей. Модель будет \"подстраиваться\" под тренировочные данные при помощи метода `fit`:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "3CvP6L-m0E2I",
        "colab": {}
      },
      "source": [
        "import numpy as np\n",
        "\n",
        "data = np.random.random((1000, 32))\n",
        "labels = np.random.random((1000, 10))\n",
        "\n",
        "model.fit(data, labels, epochs=10, batch_size=32)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "N-pnVaFe0E2N"
      },
      "source": [
        "У `tf.keras.Model.fit` есть три важных аргумента:\n",
        "\n",
        "* `epochs`: Процесс обучения структурирован по *эпохам*. Эпоха означает один проход по всему набору входных данных, который происходит небольшими порциями в батчах\n",
        "* `batch_size`: Когда мы используем массивы NumPy модель делит данные на небольшие батчи и затем выполняет указанное количество проходов. Это число определяет размер батча. Помни, что последний батч может быть меньше если общее количество образцов данных не делится на размер батча\n",
        "* `validation_data`: Когда мы строим прототип модели, мы хотим легко наблюдать за ее точностью на проверочных данных. При помощи данного аргумента - обычно кортеж или метка - модель будет отображать потери и статистику в режиме выводов inference для прошедших через модель данных в конце каждой эпохи\n",
        "\n",
        "Вот пример использования `validation_data`:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "gFcXzVQa0E2N",
        "colab": {}
      },
      "source": [
        "import numpy as np\n",
        "\n",
        "data = np.random.random((1000, 32))\n",
        "labels = np.random.random((1000, 10))\n",
        "\n",
        "val_data = np.random.random((100, 32))\n",
        "val_labels = np.random.random((100, 10))\n",
        "\n",
        "model.fit(data, labels, epochs=10, batch_size=32,\n",
        "          validation_data=(val_data, val_labels))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "-6ImyXzz0E2Q"
      },
      "source": [
        "### Используем датасеты tf.data\n",
        "\n",
        "Чтобы обучать модель на больших датасетах или на нескольких устройствах мы можем воспользоваться [Datasets API](./datasets.md). Просто добавь `tf.data.Dataset` к методу `fit`:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "OziqhpIj0E2R",
        "colab": {}
      },
      "source": [
        "# Инициализируем пробную инстанцию датасета:\n",
        "dataset = tf.data.Dataset.from_tensor_slices((data, labels))\n",
        "dataset = dataset.batch(32)\n",
        "dataset = dataset.repeat()\n",
        "\n",
        "# Не забудь указать количество шагов в каждой эпохе `steps_per_epoch` при использовании метода `fit`\n",
        "model.fit(dataset, epochs=10, steps_per_epoch=30)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "I7BcMHkB0E2U"
      },
      "source": [
        "Таким образом, метод `fit` использует аргумент `steps_per_epoch` - это количество шагов обучения, которые модель должна сделать прежде, чем перейти на следующую эпоху. Поскольку `Dataset` принимает батчи данных, то в этом примере кода нам не требуется указывать размер батча в `batch_size`.\n",
        "\n",
        "Также `dataset` можно использовать для проверки точности модели:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "YPMb3A0N0E2V",
        "colab": {}
      },
      "source": [
        "dataset = tf.data.Dataset.from_tensor_slices((data, labels))\n",
        "dataset = dataset.batch(32).repeat()\n",
        "\n",
        "val_dataset = tf.data.Dataset.from_tensor_slices((val_data, val_labels))\n",
        "val_dataset = val_dataset.batch(32).repeat()\n",
        "\n",
        "model.fit(dataset, epochs=10, steps_per_epoch=30,\n",
        "          validation_data=val_dataset,\n",
        "          validation_steps=3)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "IgGdlXso0E2X"
      },
      "source": [
        "### Оценка и предсказание\n",
        "\n",
        "Методы `tf.keras.Model.evaluate` и `tf.keras.Model.predict` могут использовать данные NumPy и `tf.data.Dataset`.\n",
        "\n",
        "Используй следующий пример кода для оценки потерь и других показателей предсказаний на использованных данных:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "mhDbOHEK0E2Y",
        "colab": {}
      },
      "source": [
        "data = np.random.random((1000, 32))\n",
        "labels = np.random.random((1000, 10))\n",
        "\n",
        "model.evaluate(data, labels, batch_size=32)\n",
        "\n",
        "model.evaluate(dataset, steps=30)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "UXUTmDfb0E2b"
      },
      "source": [
        "Для *предсказания* вывода последнего слоя как массив NumPy, используй следующий код:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "9e3JsSoQ0E2c",
        "colab": {}
      },
      "source": [
        "result = model.predict(data, batch_size=32)\n",
        "print(result.shape)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Dmj899x4hTGT"
      },
      "source": [
        "## Построение сложных моделей"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "fzEOW4Cn0E2h"
      },
      "source": [
        "### Функциональный API\n",
        "\n",
        "Последовательная модель `tf.keras.Sequential` представляет из себя обычное наложение или стек слоев, которые не могут представлять произвольные модели. Используй\n",
        "[функциональный API Keras ](https://keras.io/getting-started/functional-api-guide/){:.external} для построения комплексных топологий моделей, например таких как:\n",
        "\n",
        "* Модели с множественными входами данных\n",
        "* Модели с множественными выводами данных\n",
        "* Модели с общими слоями, где один и тот же слой вызывается несколько раз\n",
        "* Модели с непоследовательным потоком данных, например где есть остаточные связи\n",
        "\n",
        "Построение модели при помощи функционального API выглядит следующим образом:\n",
        "\n",
        "1. Слой вызывается и возвращает тензор\n",
        "2. Для определения `tf.keras.Model` используем входные и выводящие тензоры\n",
        "3. Модель обучается точно так же, как и `Sequential`\n",
        "\n",
        "Следующий пример показывает как использовать функциональный API для построения простой полносвязной сети:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "mROj832r0E2i",
        "colab": {}
      },
      "source": [
        "# Возвращает тензор-\"заполнитель\", который мы используем в качестве примера\n",
        "inputs = tf.keras.Input(shape=(32,))\n",
        "\n",
        "# Вызываемый на тензор слой, возвращает тензор\n",
        "x = layers.Dense(64, activation='relu')(inputs)\n",
        "x = layers.Dense(64, activation='relu')(x)\n",
        "predictions = layers.Dense(10, activation='softmax')(x)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "AFmspHeG1_W7"
      },
      "source": [
        "Обозначим вход и вывод данных нашей модели:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "5k5uzlyu16HM",
        "colab": {}
      },
      "source": [
        "model = tf.keras.Model(inputs=inputs, outputs=predictions)\n",
        "\n",
        "# При помощи метода `compile` настраиваем конфигурацию обучения\n",
        "model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),\n",
        "              loss='categorical_crossentropy',\n",
        "              metrics=['accuracy'])\n",
        "\n",
        "# Тренируем модель в течение 5 эпох\n",
        "model.fit(data, labels, batch_size=32, epochs=5)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "EcKSLH3i0E2k"
      },
      "source": [
        "### Создание подклассов моделей\n",
        "\n",
        "Также ты можешь создать модель с нуля при помощи подклассов в `tf.keras.Model` и определения собственных прямых проходов. Создавай слои в методе `__init__` и установи их как атрибуты класса. Прямой проход должен быть указан в методе `call`.\n",
        "\n",
        "Создание подклассов моделей особенно полезно, когда активирован [eager execution](./eager.md), так как прямой проход в этом случае всегда будет записан.\n",
        "\n",
        "Ключевой момент: всегда используй правильный API для решения конкретной задачи. Поскольку создание подклассов модели предполагает определенную гибкость, эта гибкость осложняет определение структуры и может повлечь больше ошибок у пользователя. Если возможно, то всегда старайся использовать функциональный API.\n",
        "\n",
        "Смотри следующий пример кода, в котором мы будем использовать подклассы `tf.keras.Model` и собственный прямой проход:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "KLiHWzcn2Fzk",
        "colab": {}
      },
      "source": [
        "class MyModel(tf.keras.Model):\n",
        "\n",
        "  def __init__(self, num_classes=10):\n",
        "    super(MyModel, self).__init__(name='my_model')\n",
        "    self.num_classes = num_classes\n",
        "    # Здесь определим слои\n",
        "    self.dense_1 = layers.Dense(32, activation='relu')\n",
        "    self.dense_2 = layers.Dense(num_classes, activation='sigmoid')\n",
        "\n",
        "  def call(self, inputs):\n",
        "    # А здесь укажем прямой проход,\n",
        "    # используя указанные выше слои (в `__init__`).\n",
        "    x = self.dense_1(inputs)\n",
        "    return self.dense_2(x)\n",
        "\n",
        "  def compute_output_shape(self, input_shape):\n",
        "    # Если ты хочешь использовать подклассы модели\n",
        "    # в функциональном стиле, тогда эта функция будет переписана\n",
        "    # во время запуска кода. В остальных случаях этот метод необязателен.\n",
        "    shape = tf.TensorShape(input_shape).as_list()\n",
        "    shape[-1] = self.num_classes\n",
        "    return tf.TensorShape(shape)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "ShDD4fv72KGc"
      },
      "source": [
        "Укажем класс новой модели:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "42C-qQHm0E2l",
        "colab": {}
      },
      "source": [
        "model = MyModel(num_classes=10)\n",
        "\n",
        "# При помощи метода `compile` настраиваем конфигурацию обучения\n",
        "model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),\n",
        "              loss='categorical_crossentropy',\n",
        "              metrics=['accuracy'])\n",
        "\n",
        "# Обучаем в течение 5 эпох\n",
        "model.fit(data, labels, batch_size=32, epochs=5)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "yqRQiKj20E2o"
      },
      "source": [
        "### Создание собственных слоев\n",
        "\n",
        "Чтобы создать свой собственный слой при помощи подклассов `tf.keras.layers.Layer` нам потребуются следующие методы:\n",
        "\n",
        "* `build`: Создает веса слоя. Чтобы добавить веса в модель используй метод `add_weight`\n",
        "* `call`: Определяет прямой проход\n",
        "* `compute_output_shape`: Определяет как вычислить выводящую форму слоя для данной входной формы\n",
        "* Также слой можно сериализировать при помощи метода `get_config` и метода класса `from_config`\n",
        "\n",
        "Вот пример использования нового слоя, в котором мы использовали `matmul` входа с матрицей ядра `kernel`:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "l7BFnIHr2WNc",
        "colab": {}
      },
      "source": [
        "class MyLayer(layers.Layer):\n",
        "\n",
        "  def __init__(self, output_dim, **kwargs):\n",
        "    self.output_dim = output_dim\n",
        "    super(MyLayer, self).__init__(**kwargs)\n",
        "\n",
        "  def build(self, input_shape):\n",
        "    shape = tf.TensorShape((input_shape[1], self.output_dim))\n",
        "    # Создаем обучаемую переменную весов для этого слоя\n",
        "    self.kernel = self.add_weight(name='kernel',\n",
        "                                  shape=shape,\n",
        "                                  initializer='uniform',\n",
        "                                  trainable=True)\n",
        "    # Обязательно вызови метод `build` в конце\n",
        "    super(MyLayer, self).build(input_shape)\n",
        "\n",
        "  def call(self, inputs):\n",
        "    return tf.matmul(inputs, self.kernel)\n",
        "\n",
        "  def compute_output_shape(self, input_shape):\n",
        "    shape = tf.TensorShape(input_shape).as_list()\n",
        "    shape[-1] = self.output_dim\n",
        "    return tf.TensorShape(shape)\n",
        "\n",
        "  def get_config(self):\n",
        "    base_config = super(MyLayer, self).get_config()\n",
        "    base_config['output_dim'] = self.output_dim\n",
        "    return base_config\n",
        "\n",
        "  @classmethod\n",
        "  def from_config(cls, config):\n",
        "    return cls(**config)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "8wXDRgXV2ZrF"
      },
      "source": [
        "Теперь создадим модель, используя наш новый слой:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "uqH-cY0h0E2p",
        "colab": {}
      },
      "source": [
        "model = tf.keras.Sequential([\n",
        "    MyLayer(10),\n",
        "    layers.Activation('softmax')])\n",
        "\n",
        "# При помощи метода `compile` настраиваем конфигурацию обучения\n",
        "model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),\n",
        "              loss='categorical_crossentropy',\n",
        "              metrics=['accuracy'])\n",
        "\n",
        "# Обучаем в течение 5 эпох\n",
        "model.fit(data, labels, batch_size=32, epochs=5)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Lu8cc3AJ0E2v"
      },
      "source": [
        "## Функции обратного вызова\n",
        "\n",
        "Функция обратного вызова `callback` - это объект, который передается модели для обработки и расширения ее поведения во время обучения. Ты можешь написать свою собственную функцию callback, или использовать готовые `tf.keras.callbacks` в которые входят:\n",
        "\n",
        "* `tf.keras.callbacks.ModelCheckpoint`: Сохраняет контрольные точки твоей модели через заданный интервал\n",
        "* `tf.keras.callbacks.LearningRateScheduler`: Замедляет темп обучения `learning rate` для получения лучших результатов точности\n",
        "* `tf.keras.callbacks.EarlyStopping`: Останавливает обучение как только точность перестает увеличиваться\n",
        "* `tf.keras.callbacks.TensorBoard`: Следит за поведением модели при помощи [TensorBoard](https://tensorflow.org/tensorboard)\n",
        "\n",
        "Чтобы использовать `tf.keras.callbacks.Callback` просто передай его в метод `fit` своей модели:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "rdYwzSYV0E2v",
        "colab": {}
      },
      "source": [
        "callbacks = [\n",
        "  # Прерывает обучение если потери при проверке `val_loss` перестают\n",
        "  # уменьшаться после 2 эпох\n",
        "  tf.keras.callbacks.EarlyStopping(patience=2, monitor='val_loss'),\n",
        "  # Записываем логи TensorBoard в папку `./logs`\n",
        "  tf.keras.callbacks.TensorBoard(log_dir='./logs')\n",
        "]\n",
        "model.fit(data, labels, batch_size=32, epochs=5, callbacks=callbacks,\n",
        "          validation_data=(val_data, val_labels))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "GK87-Dhv2CCN"
      },
      "source": [
        "<a id='weights_only'></a>\n",
        "## Сохранение и загрузка"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "qnl7K-aI0E2z"
      },
      "source": [
        "### Сохраняем веса\n",
        "\n",
        "Ты можешь сохранять и загружать веса модели при помощи `tf.keras.Model.save_weights`:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "uQIANjB94fLB",
        "colab": {}
      },
      "source": [
        "model = tf.keras.Sequential([\n",
        "layers.Dense(64, activation='relu'),\n",
        "layers.Dense(10, activation='softmax')])\n",
        "\n",
        "model.compile(optimizer=tf.train.AdamOptimizer(0.001),\n",
        "              loss='categorical_crossentropy',\n",
        "              metrics=['accuracy'])"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "4eoHJ-ny0E21",
        "colab": {}
      },
      "source": [
        "# Сохраняем веса в контрольную точку формата TensorFlow\n",
        "model.save_weights('./weights/my_model')\n",
        "\n",
        "# Восстанавливаем состояние модели,\n",
        "# требуется использование модели с точно такой же архитектурой\n",
        "model.load_weights('./weights/my_model')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "u25Id3xe0E25"
      },
      "source": [
        "По умолчанию, веса модели сохраняются в формате [контрольой точки TensorFlow](./checkpoints.md). Веса также могут быть сохранены в формате Keras HDF5, который является стандартным в бэкенд структуре Keras:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "JSAYoFEd0E26",
        "colab": {}
      },
      "source": [
        "# Сохраняем веса в файл HDF5\n",
        "model.save_weights('my_model.h5', save_format='h5')\n",
        "\n",
        "# Загружаем текущее состояние модели\n",
        "model.load_weights('my_model.h5')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "mje_yKL10E29"
      },
      "source": [
        "### Сохраняем конфигурацию\n",
        "\n",
        "Конфигурация модели также может быть сохранена: такой метод сериализирует архитектуру модели без сохранения весов.\n",
        "\n",
        "Сохраненная конфигурация можеть быть загружена и инициализирована как оригинальная модель, даже без кода изначальной модели.\n",
        "\n",
        "Keras поддерживает форматы сериализации данных JSON и YAML:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "EbET0oJTzGkq",
        "colab": {}
      },
      "source": [
        "# Сериализация модели в формат JSON\n",
        "json_string = model.to_json()\n",
        "json_string"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "pX_badhH3yWV",
        "colab": {}
      },
      "source": [
        "import json\n",
        "import pprint\n",
        "pprint.pprint(json.loads(json_string))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Q7CIa05r4yTb"
      },
      "source": [
        "Давай воссоздадим только что инициализированную модель из JSON:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "J9UFv9k00E2_",
        "colab": {}
      },
      "source": [
        "fresh_model = tf.keras.models.model_from_json(json_string)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "t5NHtICh4uHK"
      },
      "source": [
        "Сериализация модели в формат YAML требуется установки `pyyaml` *до импорта TensorFlow*:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "aj24KB3Z36S4",
        "colab": {}
      },
      "source": [
        "yaml_string = model.to_yaml()\n",
        "print(yaml_string)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "O53Kerfl43v7"
      },
      "source": [
        "Восстановим модель из формата YAML:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "77yRuwg03_MG",
        "colab": {}
      },
      "source": [
        "fresh_model = tf.keras.models.model_from_yaml(yaml_string)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "xPvOSSzM0E3B"
      },
      "source": [
        "Важно: модели с подклассами не могут быть сериализированы, потому что их архитектура определяется кодом Python в методе `call`."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "iu8qMwld4-71"
      },
      "source": [
        "### Сохраняем модель полностью\n",
        "\n",
        "Мы можем сохранить модель целиком в один файл, который будет содержать веса, конфигурацию модели и даже настройки оптимизатора. Это позволяет сохранить модель как контрольную точку и продолжить обучение позже - ровно с того же момента и без доступа к исходному коду."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "45oNY34Z0E3C",
        "colab": {}
      },
      "source": [
        "# Создаем простую модель\n",
        "model = tf.keras.Sequential([\n",
        "  layers.Dense(10, activation='softmax', input_shape=(32,)),\n",
        "  layers.Dense(10, activation='softmax')\n",
        "])\n",
        "model.compile(optimizer='rmsprop',\n",
        "              loss='categorical_crossentropy',\n",
        "              metrics=['accuracy'])\n",
        "model.fit(data, labels, batch_size=32, epochs=5)\n",
        "\n",
        "# Сохраняем модель целиком в один файл формата HDF5\n",
        "model.save('my_model.h5')\n",
        "\n",
        "# Восстаналиваем ту же самую модель, включая веса и оптимизатор\n",
        "model = tf.keras.models.load_model('my_model.h5')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "PMOWhDOB0E3E"
      },
      "source": [
        "## Eager execution\n",
        "\n",
        "[Eager execution](./eager.md) - это окружение императивного программирования, в котором все операции вычисляются мгновенно. В нем не требуется Keras, однако `tf.keras` поддерживается и оказывается весьма полезен для проверки программы и отладки кода.\n",
        "\n",
        "Все API `tf.keras` для построения моделей совместимы с eager execution. В то время как функциональный API и `Sequential` могут быть использованы и здесь, наибольшее преимущество получат *модели с подклассами* и *модели с собственными слоями* - это именно те API, в которых тебе необходимо указать прямой проход в виде кода (вместо тех API, которые создают модели посредством сборки существующих слоев).\n",
        "\n",
        "Смотри [Руководство по eager execution](./eager.md#build_a_model) для ознакомления с примерами использования моделей Keras с уникальными циклами обучения `tf.GradientTape`."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "H1jB-t138gQc"
      },
      "source": [
        "## Распределенное обучение"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "2wG3NVco5B5V"
      },
      "source": [
        "### Алгоритмы оценки Estimators\n",
        "\n",
        "\n",
        "API [Estimators](./estimators.md) используется для обучения моделей в распределенном окружении. Он необходим для решения задач распределенного обучения на больших датасетах например для экспорта модели в продакшен.\n",
        "\n",
        "`tf.keras.Model` может обучаться с API `tf.estimator` посредством конвертации модели в объект `tf.estimator.Estimator` при помощи `tf.keras.estimator.model_to_estimator`. Читай больше в статье [Создание Estimators из моделей Keras](./estimators.md#creating_estimators_from_keras_models)."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "cVg0vfTO0E3E",
        "colab": {}
      },
      "source": [
        "model = tf.keras.Sequential([layers.Dense(10,activation='softmax'),\n",
        "                          layers.Dense(10,activation='softmax')])\n",
        "\n",
        "model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),\n",
        "              loss='categorical_crossentropy',\n",
        "              metrics=['accuracy'])\n",
        "\n",
        "estimator = tf.keras.estimator.model_to_estimator(model)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "S7FKvikO0E3H"
      },
      "source": [
        "Совет: используй [eager execution](./eager.md) для отладки\n",
        "[функций входа Estimator](./premade_estimators.md#create_input_functions) и проверки данных."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "6PJZ6e9J5JHF"
      },
      "source": [
        "### Обучение на нескольких GPU\n",
        "\n",
        "Модели `tf.keras` могут обучаться на нескольких GPU при помощи\n",
        "`tf.contrib.distribute.DistributionStrategy`. Этот API обеспечивает распределенное обучение на нескольких GPU почти без изменений основного кода модели.\n",
        "\n",
        "В настоящее время `tf.contrib.distribute.MirroredStrategy` является единственной поддерживаемой стратегией распределенного обучения. `MirroredStrategy` выполняет внутриграфную репликацию с синхронным обучением, используя функцию all-reduce на одном устройстве. Чтобы использовать `DistributionStrategy` вместе с Keras, конвертируй модель `tf.keras.Model` в `tf.estimator.Estimator` при помощи `tf.keras.estimator.model_to_estimator`, а затем обучи получившийся estimator.\n",
        "\n",
        "В следующем примере мы посмотрим как распределить `tf.keras.Model` на нескольких GPU на одном устройстве.\n",
        "\n",
        "Для начала определим простую модель:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "sbaRr7g-0E3I",
        "colab": {}
      },
      "source": [
        "model = tf.keras.Sequential()\n",
        "model.add(layers.Dense(16, activation='relu', input_shape=(10,)))\n",
        "model.add(layers.Dense(1, activation='sigmoid'))\n",
        "\n",
        "optimizer = tf.train.GradientDescentOptimizer(0.2)\n",
        "\n",
        "model.compile(loss='binary_crossentropy', optimizer=optimizer)\n",
        "model.summary()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "yw4hSJme0E3L"
      },
      "source": [
        "Затем определим функцию *загрузки и обработки данных*. Функция `Input_fn` возвращает объект `tf.data.Dataset`, который используется для распределения данных на нескольких устройствах, где каждый GPU обрабатывает свой входящий батч."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "CxJW1QMH0E3L",
        "colab": {}
      },
      "source": [
        "def input_fn():\n",
        "  x = np.random.random((1024, 10))\n",
        "  y = np.random.randint(2, size=(1024, 1))\n",
        "  x = tf.cast(x, tf.float32)\n",
        "  dataset = tf.data.Dataset.from_tensor_slices((x, y))\n",
        "  dataset = dataset.repeat(10)\n",
        "  dataset = dataset.batch(32)\n",
        "  return dataset"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "rO9MiL6X0E3O"
      },
      "source": [
        "Далее, создадим `tf.estimator.RunConfig` и передадим аргумент `train_distribute`\n",
        "к  `tf.contrib.distribute.MirroredStrategy`. При создании\n",
        "`MirroredStrategy` ты можешь определить список устройств или передать аргумент `num_gpus` с заданным количеством GPU для обучения. По умолчанию используются все доступные GPU как в следующем примере:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "BEwFq4PM0E3P",
        "colab": {}
      },
      "source": [
        "strategy = tf.contrib.distribute.MirroredStrategy()\n",
        "config = tf.estimator.RunConfig(train_distribute=strategy)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "TcnwYVun0E3R"
      },
      "source": [
        "Конвертируем модель Keras в `tf.estimator.Estimator`:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "VSvbuIID0E3S",
        "colab": {}
      },
      "source": [
        "keras_estimator = tf.keras.estimator.model_to_estimator(\n",
        "  keras_model=model,\n",
        "  config=config,\n",
        "  model_dir='/tmp/model_dir')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "N6BXU5F90E3U"
      },
      "source": [
        "Наконец, обучаем `Estimator`, передав аргументы `input_fn` и `steps`:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "XKoJ2wUH0E3U",
        "colab": {}
      },
      "source": [
        "keras_estimator.train(input_fn=input_fn, steps=10)"
      ],
      "execution_count": 0,
      "outputs": []
    }
  ]
}